/*
 * 通讯协议接口实现类。
 * Copyright (C) 2009  YuLicheng
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.sysmo.communication;

import java.io.IOException;
import java.net.InetAddress;
import javax.net.ssl.SSLSocket;

/**
 * 
 * 通讯协议接口实现。此通讯接口是服务器和客户端通用的接口。此类实现了客户端和服务器
 * 之间的长时间连接，并且通过心跳包来维持长连接不中断。也就是每隔一段时间一方会发送
 * 心跳包到对方，此时间可通过{@link #setHeartbeatTime(int)}方法指定。注意：为保持
 * 连接，心跳包的发送是自动的，但是心跳包的接受不是，接受心跳包的一方必须已经调用了
 * {@link #getByteArray()}或{@link #getString()}方法，这两个方法会自动接收并忽略
 * 心跳包，因此一般情况下总是应该持续调用这两个方法中的一个以保证持续接收消息，并且
 * 自动处理心跳包。
 * <br />
 *
 * 此类实现了SSL加密连接。使用此类时需要提供相应的证书文件和密码（客户端只需要证书
 * 不需要密码）。证书文件可使用jdk或jre的keytool实用程序创建。
 * 可使用以下命令行生成证书：
 * <br />
 *
 * <code>keytool –genkey –keystore 证书名 –keyalg rsa –alias 别名</code>
 * <br />
 * 
 * 对所有后续的问题保持默认即可。生成证书后，服务器端和每一个客户端都需要一个证书副
 * 本，这样服务器和客户端之间才能建立安全连接。
 * <br />
 *
 * 注意：证书是有有效期的，超过有效期的证书将无法使用，需要重新使用keytool工具生成
 * 新证书。
 * <br />
 *
 * 首先可由服务器调用此类的静态方法{@link #waitForConnection(String, String, int)}
 * 在指定端口等待客户端连接。若有客户端连接成功则返回相应的Socket对象。此时服务器应
 * 将以此Socket为参数构造的此类实例交给相应的客户端节点对象处理，而服务器核心继续调
 * 用此方法等待新连接。
 * <br />
 *
 * 客户端可使用以证书名、服务器网络地址和监听端口为参数构造此类实例，然后调用
 * {@link #tryConnect() }方法尝试与服务器连接。若由于服务器没有启动等原因无法连接
 * 服务器则客户端可不断调用此方法持续尝试连接。
 * <br />
 *
 * 在连接成功建立后双方都可调用{@link #sendByteArray(byte[])}或
 * {@link #sendString(String)}方法给对方发送二进制数据或者字符串。另一方可调用相应
 * 的{@link #getByteArray()}或{@link #getString()}方法获得对方发送的信息。
 * <br />
 *
 * 注意：发送方和接收方应制定高层的通讯协议，每次发送和接收时都应保证对方会处于等待
 * 接收或发送的阶段。也就是说双方都应该一直在监听对方发送的数据，例如可用字符串方式
 * 发送控制命令。例如，当A发送传送文件请求到B后，A可使用sendByteArray方法将相应的文
 * 件数据发送到B，B通过getString方式得到A之前发送的传送文件请求后会调用相应的
 * getByteArray方法接收A发送的数据，而在文件发送完成后，B又会继续调用getString方法
 * 等待A的下一个命令。因此，数据传输的语义同步是由外部逻辑逻辑实现的。且连接双方之间
 * 总是只有一个连接通道，所有的数据传输都必须串行进行。
 * <br />
 *
 * SSL连接参考：http://java.ccidnet.com/art/3737/20060808/789375_1.html
 * <br />
 *
 * Keytool工具参考：http://www.javaeye.com/topic/50301
 *
 * @author YuLicheng
 */
public class CommunicationInterface {

    //以下两个Object用于发送和接收数据时同步。
    private final Object inputLock = new Object();
    private final Object outputLock = new Object();

    /**
     * 创建新连接，连接到指定地址正监听的端口。此构造方法只是设置相应的参数，若使用
     * 此方法创建了此类的实例后，应使用{@link #tryConnect()}方法开始尝试连接。一
     * 般由客户端调用此方法来创建到服务器的连接对象。注意在连接前，客户端必须已经获
     * 得了有效的证书文件。
     * <br />
     *
     * 使用此方法创建的对象将会每隔一段时间自动发送心跳包以保持和服务器保持长连接。
     * 默认的心跳包发送间隔请看{@link #setHeartbeatTime(int)}方法说明。
     * <br />
     *
     * 证书文件和加密连接的说明请参看本类开头的说明。
     *
     * @param signFile SSL证书文件。
     * @param address 服务器地址。
     * @param port 服务器监听的端口号。
     */
    public CommunicationInterface(String signFile, InetAddress address,
            int port) {

    }

    /**
     * 使用已有的Socket创建连接。此构造方法只是设置相应的参数，但由于Socket已经创
     * 建，可直接调用接受和发送数据的方法来开始通讯。此方法一般由服务器端的节点对象
     * 调用。参数中的socket是由{@link #waitForConnection(String, String, int)}
     * 方法返回的。
     * <br />
     *
     * 使用此方法创建的对象不会自动发送心跳包（发送时间间隔默认设为0），但是会在调用
     * {@link #getByteArray()}或{@link #getString()}方法中自动接收并忽略心跳包，
     * 因此当连接建立后，应该总是持续调用这两个方法之一以保证心跳包能够被处理，长连
     * 接不会中断。
     *
     * @param socket 已创建的连接SSLSocket对象。
     */
    public CommunicationInterface(SSLSocket socket) {
        
    }

    /**
     * 创建服务器监听。此方法由服务器端调用，会使用相应的证书和密码，在服务器的指定
     * 端口监听客户端连接，若有客户端连接则成功返回相应的SSLSocket。
     * <br />
     *
     * 此方法在接受新客户端连接后会立即返回，服务器应该重新调用此方法以持续监听新连
     * 接。
     * <br />
     *
     * 证书文件和加密连接的说明请参看本类开头的说明。
     *
     * @param signFile 证书文件。
     * @param password 证书密码。
     * @param port 要监听的端口。
     * @return 连接的客户端SSLSocket。
     * @throws IOException 连接失败会返回此异常。
     */
    public static SSLSocket waitForConnection(String signFile,
            String password, int port) throws IOException {
        return null;
    }

    /**
     * 尝试连接服务器端。此方法由客户端调用，客户端应该以服务器地址、端口创建此类的
     * 实例，然后调用此方法来尝试连接服务器。若连接不成功（可能是由于服务器已关闭），
     * 则用户端可持续调用此方法来一直尝试连接。
     *
     * @return 若连接成功则返回true，否则返回false。
     */
    public boolean tryConnect() {
        return false;
    }

    /**
     * 返回当前连接的状态。某些情况下连接已断开，但是仍有可能返回true。
     *
     * @return 若已连接，则返回true。
     */
    public boolean isConnected() {
        return false;
    }

    /**
     * 关闭当前连接。
     */
    public void closeConnection() {
        
    }

    /**
     * 获取当前连接的Socket对象。
     * 
     * @return Socket对象。
     */
    public SSLSocket getSocket() {
        return null;
    }

    /**
     * 设置心跳包的发送时间间隔。若在这里指定的时间内连接双方都没有发送数据，则由一
     * 方自动发送心跳包到对方，对方此时应该已经调用了{@link #getByteArray()}或
     * {@link #getString()}方法在等待对方发送数据，这两个方法会自动忽略心跳包。
     * <br />
     *
     * 一般由客户端负责发送心跳包，如果此类实例是通过使用服务器地址、端口等参数的构
     * 造方法创建，则默认心跳包发送时间为5分钟。若是使用Socket作为参数创建的实例则
     * 默认不发送心跳包（即发送时间间隔为0，一般为服务器）。
     * <br />
     *
     * 可通过此方法来改变上述默认动作。将间隔时间设为0则从不发送心跳包。
     *
     * @param minute 心跳包发送间隔，单位是分钟。
     */
    public void setHeartbeatTime(int minute) {
        
    }

    /**
     * 监听对方发送的消息，返回消息字符串。连接创建后，应持续调用此方法以保证能够
     * 接收到对方随时发出的请求，并且能够处理心跳包以保证长连接不断开。
     * <br />
     *
     * 此方法一旦调用将一直等待到对方有数据发送才会返回相应的数据，或者调用了
     * {@link #closeConnection()}方法，否则将一直等待。在此期间若收到心跳包则将接
     * 收并忽略，然后继续等待信息。
     * <br />
     *
     * 此方法为同步方法，由于只有一个连接通道，同一时刻只能有一个线程调用此方法，其
     * 他线程都将阻塞直至之前的线程调用此方法结束，以保证所有数据的发送或接收都是串
     * 行的。注意：读取数据和发送数据是分开同步的。
     * <br />
     *
     * 此方法的语义同步应有外部的高层协议实现，此方法并不区分对方发送的内容是否是字
     * 符串还是二进制数据。
     *
     * @return 收到的字符串。
     * @throws IOException 若发生错误，则抛出此异常。
     */
    public String getString() throws IOException {
        synchronized (inputLock) {
            return null;
        }
    }


    /**
     * 向对方发送字符串。
     * <br />
     * 
     * 此方法为同步方法，由于只有一个连接通道，同一时刻只能有一个线程调用此方法，其
     * 他线程都将阻塞直至之前的线程调用此方法结束，以保证所有数据的发送或接收都是串
     * 行的。注意：读取数据和发送数据是分开同步的。
     * <br />
     *
     * 此方法的语义同步应有外部的高层协议实现，此方法并不区分对方发送的内容是否是字
     * 符串还是二进制数据。
     *
     * @param message 要发送的字符串。
     * @throws IOException 若发生错误，则抛出此异常。
     */
    public void sendString(String message) throws IOException {
        synchronized (outputLock) {
            
        }
    }

    /**
     * 监听对方发送的消息，返回二进制数据。
     * <br />
     *
     * 此方法一旦调用将一直等待到对方有数据发送才会返回相应的数据，或者调用了
     * {@link #closeConnection()}方法，否则将一直等待。在此期间若收到心跳包则将
     * 接收并忽略，然后继续等待信息。
     * <br />
     *
     * 此方法为同步方法，由于只有一个连接通道，同一时刻只能有一个线程调用此方法，其
     * 他线程都将阻塞直至之前的线程调用此方法结束，以保证所有数据的发送或接收都是串
     * 行的。注意：读取数据和发送数据是分开同步的。
     * <br />
     *
     * 此方法的语义同步应有外部的高层协议实现，此方法并不区分对方发送的内容是否是字
     * 符串还是二进制数据。
     *
     * @return 收到的二进制数据。
     * @throws IOException 若发生错误，则抛出此异常。
     */
    public byte[] getByteArray() throws IOException {
        synchronized (inputLock) {
            return null;
        }
    }

    /**
     * 向对方发送二进制数据。
     * <br />
     *
     * 此方法为同步方法，由于只有一个连接通道，同一时刻只能有一个线程调用此方法，其
     * 他线程都将阻塞直至之前的线程调用此方法结束，以保证所有数据的发送或接收都是串
     * 行的。注意：读取数据和发送数据是分开同步的。
     * <br />
     *
     * 此方法的语义同步应有外部的高层协议实现，此方法并不区分对方发送的内容是否是字
     * 符串还是二进制数据。
     *
     * @param data 要发送的数据。
     * @throws IOException 若出错则抛出此异常。
     */
    public void sendByteArray(byte[] data) throws IOException {
        synchronized (outputLock) {
            
        }
    }

}
